﻿// Copyright 2008 William P. Hickey Jr. and William U. Walker
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//     
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.IO;
using System.Media;
using AgwpePort;
using AgwpePort.Aprs;
using System.Deployment.Application;

namespace CSReporter
{
    public partial class MainForm : Form
    {
        // Static initialization ensures thread safety.
        private static readonly MainForm singletonInstance = new MainForm();
        private int selectedMessageFolder;
        private ConfigList configure;
        private StatusTextList statusText;
        private StationList stations;
        private MessageList messages;
        private CSReportList csReports;
        private AprsPacketList packets;
        private int printMore;
        private Radio radio;
        private MapForm map;

        #region " Constructor and Load event handler "
        protected MainForm()
        {
            InitializeComponent();
        }

        public static MainForm GetMainForm()
        {
            return singletonInstance;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            // Get the ConfigList object.
            configure = ConfigList.GetConfigList();
            configure.ReadXml();
            // Get the StatusTextList object.
            statusText = StatusTextList.GetStatusTextList();
            statusText.ReadXml(configure[0].DataDir + @"\statustext.xml");
            // Get the StationList object.
            stations = StationList.GetStationList();
            stations.ReadXml(configure[0].DataDir + @"\stations.xml");
            // Get the MesssageList object.
            messages = MessageList.GetMessageList();
            // Get the AprsPacketList object.
            packets = AprsPacketList.GetAprsPacketList();
            // Create a new Radio object to handle packet communications.
            radio = Radio.GetRadio();
            // Set the startup message folder to the inbox folder.
            selectedMessageFolder = 0;

            // This is test code only -----------------------------------------
            //status.WriteXml(configure[0].DataDir + @"\statustext.xml");
            //messages.AddMessage(new AprsMessage(DateTime.Now.AddMinutes(-130), "AB7AA", "W5ZIO-1", "Test message", 0, "AB7FE", "", false));
            //messages.AddMessage(new AprsMessage(DateTime.Now.AddMinutes(-129), "W5ZIO", "AB7AA", "Test message", 2, "AB7FF", "", false));
            //messages.AddMessage(new AprsMessage(DateTime.Now.AddMinutes(-120), "W5ZIO-1", "AB7AA", "Test message", 2, "AB8A0", "", false));
            //messages.AddMessage(new AprsMessage(DateTime.Now.AddMinutes(-90), "N2MZ", "AB7AA", "Test message", 3, "AB8A1", "", false));
            //messages.AddMessage(new AprsMessage(DateTime.Now.AddMinutes(-1530), "W5ZIO-1", "W5ZIO", "^1W5ZIO-1   0002091604z3211.94N11052.39WWWWWWWWWGWGWGWWW      TUSAZ", 6, "17707", "", true));
            //messages.AddMessage(new AprsMessage(DateTime.Now.AddMinutes(-1529.95), "W5ZIO-1", "W5ZIO", "^2W5ZIO-1   00025609,E,30th", 6, "17708", "", true));
            //messages.AddMessage(new AprsMessage(DateTime.Now.AddMinutes(-1529.9), "W5ZIO-1", "W5ZIO", "^CW5ZIO-1   0002All is cool!", 6, "1770A", "", true));
            //messages.AddMessage(new AprsMessage(DateTime.Now.AddMinutes(-13), "W5ZIO-1", "EOC", "^1AB7AA     0003092023z3211.94N11052.39WWWWWWWWWWGWWRWYW      TUSAZ", 6, "18707", "", true));
            //messages.AddMessage(new AprsMessage(DateTime.Now.AddMinutes(-12.95), "W5ZIO-1", "EOC", "^2AB7AA     00035609,E,30th", 6, "18708", "", true));
            //messages.AddMessage(new AprsMessage(DateTime.Now.AddMinutes(-12.9), "W5ZIO-1", "EOC", "^CAB7AA     0003All is NOT cool!", 6, "1870A", "", true));
            //SaveMessages();
            // ----------------------------------------------------------------

            // Load data into the MessageList object.
            LoadMessages();
            // Load messages into the MessageDataGridView component for the currently selected message folder.
            LoadMessageDataGridView(selectedMessageFolder);
            // Load and display community status reports.
            LoadCSReports();
            // Check startup options
            if (configure[0].ConnectOnStart)
            {
                agwConnectToolStripButton_Click(this, null);
                if (configure[0].BeaconOnStart)
                    beaconToolStripButton_Click(this, null);
            }
        }

        private void MainForm_Shown(object sender, EventArgs e)
        {
            // Set the toolbar printer tooltip text to show the default printer.
            printToolStripButton.ToolTipText = "Print (" + PrintDocument.PrinterSettings.PrinterName + ")";
            // Set the window title text.
            this.Text = "CSReporter - 0.5.0.15";

            // If KML file configuration information is complete...
            if (configure[0].KmlFileLocation.Trim().Length > 0 &
                configure[0].KmlGreenIcon.Trim().Length > 0 &
                configure[0].KmlRedIcon.Trim().Length > 0 &
                configure[0].KmlWhiteIcon.Trim().Length > 0 &
                configure[0].KmlYellowIcon.Trim().Length > 0)
            {
                // Enable the menu item and toolbar icon.
                publishKMLToolStripButton.Enabled = true;
                PublishKMLFileToolStripMenuItem.Enabled = true;
            }
            else
            {
                // Otherwise disable the menu item and toolbar icon.
                publishKMLToolStripButton.Enabled = false;
                PublishKMLFileToolStripMenuItem.Enabled = false;
            }
            
            // If AGWPE configuration information is complete...
            if (configure[0].AgwHost.Trim().Length > 0 &
                configure[0].AgwPort > 0 &
                configure[0].AgwVhfUhfRadio >= 0 &
                configure[0].CallSign.Trim().Length > 0 &
                configure[0].InterfaceType != PacketControlType.Disabled)
            {
                agwConnectToolStripButton.Enabled = true;
            }
            else
            {
                agwConnectToolStripButton.Enabled = false;
            }
            beaconToolStripButton.Enabled = false;
            beaconToolStripButton.ToolTipText = "Enable Beacon Transmissions";
            
            // Disable message toolbar icons.
            newMessageToolStripButton.Enabled = false;
            replyToolStripButton.Enabled = false;
            forwardToolStripButton.Enabled = false;

        }
        #endregion
        #region " Button and MenuItem Click event handlers "
        public void ReloadReportDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Load and display community status reports.
            LoadCSReports();
        }

        private void DataFilesToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void SQLDatabaseToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void SaveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            StreamWriter fileStream;

            mainSaveFileDialog.ShowDialog();

            if (mainSaveFileDialog.FileName.Length > 0)
            {
                switch(mainSaveFileDialog.FilterIndex)
                {
                    case 1: // Rich Text file
                        detailRichTextBox.SaveFile(mainSaveFileDialog.FileName, RichTextBoxStreamType.RichText);
                        break;
                    case 2: // Text file
                        fileStream = new StreamWriter(mainSaveFileDialog.FileName);
                        fileStream.Write(detailRichTextBox.Text);
                        fileStream.Close();
                        break;
                    case 3: // HTML file
                        fileStream = new StreamWriter(mainSaveFileDialog.FileName);
                        fileStream.Write(detailRichTextBox.Html);
                        fileStream.Close();
                        break;
                    case 4: // Google Earth KML file
                        WriteKml(mainSaveFileDialog.FileName);
                        break;
                    case 5: // XML file
                        csReports.WriteXml(mainSaveFileDialog.FileName);
                        break;
                }
            }
        }

        private void PublishKMLFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            WriteKml(Path.GetFullPath(configure[0].KmlFileLocation).TrimEnd('\\') + "\\" + configure[0].EventName.Trim() + "_features.kml");
            //BackgroundWorker1.RunWorkerAsync();
        }

        private void PageSetupToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PageSetupDialog.ShowDialog();
        }

        private void PrintPreviewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PrintPreviewDialog.ShowDialog();
        }

        private void PrintToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (PrintDialog.ShowDialog() == DialogResult.OK)
                printToolStripButton_Click(this, null);
        }

        private void ExitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void NewESPReportToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ReportForm rf = new ReportForm();

            // Show the status report dialog box.
            if(!rf.IsDisposed)
                rf.Show();
        }

        private void EditStationsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            EditStationsDialog esd = new EditStationsDialog();
            // Show the station edit dialog box.
            esd.ShowDialog();
        }

        private void OptionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CustomizeDialog cd = new CustomizeDialog();

            if(cd.ShowDialog() == DialogResult.OK)
            {
                // If KML file configuration information is complete...
                if (configure[0].KmlFileLocation.Trim().Length > 0 &
                    configure[0].KmlGreenIcon.Trim().Length > 0 &
                    configure[0].KmlRedIcon.Trim().Length > 0 & 
                    configure[0].KmlWhiteIcon.Trim().Length > 0 &
                    configure[0].KmlYellowIcon.Trim().Length > 0)
                {
                    // Enable the menu item and toolbar icon.
                    publishKMLToolStripButton.Enabled = true;
                    PublishKMLFileToolStripMenuItem.Enabled = true;
                }
                else
                {
                    // Otherwise disable the menu item and toolbar icon.
                    publishKMLToolStripButton.Enabled = false;
                    PublishKMLFileToolStripMenuItem.Enabled = false;
                }
                // If any radio interface perameters have changed...
                if (configure[0].AgwHost.Trim() != radio.HostName.Trim() |
                    configure[0].AgwPort != radio.HostPort |
                    configure[0].AgwVhfUhfRadio != radio.RadioPort |
                    configure[0].InterfaceType != radio.InterfaceType |
                    configure[0].CallSign.Trim() != radio.CallSign.Trim())
                {
                    if (radio.IsOpen)
                    {
                        // Close the interface connection
                        agwConnectToolStripButton_Click(this, null);
                        // Let the user know the radio is no longer connected.
                        MessageBox.Show("Radio interface parameters have changed. You will need to reconnect to the radio interface for changes to take effect.", 
                            "Configuration Change Detected", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    }
                    // Use the new perameters.
                    radio.HostName = configure[0].AgwHost.Trim();
                    radio.HostPort = configure[0].AgwPort;
                    radio.RadioPort = configure[0].AgwVhfUhfRadio;
                    radio.InterfaceType = configure[0].InterfaceType;
                    radio.CallSign = configure[0].CallSign.Trim();
                }
                // If AGWPE configuration information is complete...
                if (configure[0].AgwHost.Trim().Length > 0 &
                    configure[0].AgwPort > 0 &
                    configure[0].AgwVhfUhfRadio >= 0 &
                    configure[0].CallSign.Trim().Length > 0 &
                    configure[0].InterfaceType != PacketControlType.Disabled)
                {
                    // Enable the menu item and toolbar icon.
                    agwConnectToolStripButton.Enabled = true;
                }
                else
                {
                    // Otherwise disable the menu item and toolbar icons.
                    agwConnectToolStripButton.Enabled = false;
                    beaconToolStripButton.Enabled = false;
                    radio.BeaconEnabled = false;
                    beaconToolStripButton.ToolTipText = "Enable Beacon Transmissions";
                }
                // Set the beacon timer interval.
                radio.BeaconInterval = configure[0].BeaconInterval * 60000;
            }
        }

        private void agwConnectToolStripButton_Click(object sender, EventArgs e)
        {
            if (!radio.IsOpen & configure[0].CallSign.Trim().Length > 0 & configure[0].AgwHost.Trim().Length > 0)
            {
                if (!radio.Connect())
                {
                    beaconToolStripButton.Enabled = false;
                    beaconToolStripButton.Image = buttonImageList.Images["transmit_blue.png"];
                    beaconToolStripButton.ToolTipText = "Enable Beacon Transmissions";
                    agwConnectToolStripButton.Image = buttonImageList.Images["disconnect.png"];
                    agwConnectToolStripButton.ToolTipText = "Connect to AGWPE";
                    statusBarLabel.Text = "Disconnected from AGWPE";
                    newMessageToolStripButton.Enabled = false;
                    replyToolStripButton.Enabled = false;
                    forwardToolStripButton.Enabled = false;
                    PlaySync(@"sounds\Deactivate.wav");
                }
            }
            else if (radio.IsOpen)
            {
                radio.Close();
                beaconToolStripButton.Enabled = false;
                beaconToolStripButton.Image = buttonImageList.Images["transmit_blue.png"];
                beaconToolStripButton.ToolTipText = "Enable Beacon Transmissions";
                agwConnectToolStripButton.Image = buttonImageList.Images["disconnect.png"];
                agwConnectToolStripButton.ToolTipText = "Connect to AGWPE";
                statusBarLabel.Text = "Disconnected from AGWPE";
                newMessageToolStripButton.Enabled = false;
                replyToolStripButton.Enabled = false;
                forwardToolStripButton.Enabled = false;
                PlaySync(@"sounds\Deactivate.wav");
            }
        }

        private void printToolStripButton_Click(object sender, EventArgs e)
        {
            PrintDialog.ShowDialog();
        }

        private void beaconToolStripButton_Click(object sender, EventArgs e)
        {
            if (radio.BeaconEnabled)
            {
                radio.BeaconEnabled = false;
                beaconToolStripButton.Image = buttonImageList.Images["transmit_blue.png"];
                beaconToolStripButton.ToolTipText = "Enable Beacon Transmissions";
            }
            else if (radio.IsOpen)
            {
                radio.SendBeacon();
                radio.BeaconInterval = configure[0].BeaconInterval * 60000;
                radio.BeaconEnabled = true;
                beaconToolStripButton.Image = buttonImageList.Images["transmit.png"];
                beaconToolStripButton.ToolTipText = "Disable Beacon Transmissions";
            }
        }

        private void newMessageToolStripButton_Click(object sender, EventArgs e)
        {
            if (radio.IsOpen)
            {
                MessageForm mf = new MessageForm();
                mf.ShowDialog();
            }
        }

        private void replyToolStripButton_Click(object sender, EventArgs e)
        {
            if (radio.IsOpen & MessageDataGridView.SelectedRows.Count == 1)
            {
                string fromCall = MessageDataGridView.CurrentRow.Cells["FromCall"].Value.ToString();

                if (fromCall.Length > 0)
                {
                    MessageForm mf = new MessageForm(fromCall);
                    mf.ShowDialog();
                }
            }
        }

        private void forwardToolStripButton_Click(object sender, EventArgs e)
        {
            if (radio.IsOpen & MessageDataGridView.SelectedRows.Count == 1)
            {
                string message = MessageDataGridView.CurrentRow.Cells["Message"].Value.ToString();

                if (message.Length > 0)
                {
                    MessageForm mf = new MessageForm("", message);
                    mf.ShowDialog();
                }
            }
        }

        private void addToStationsListToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Station station = new Station();
            station.CallSign = StatusReportDataGridView.CurrentRow.Cells["Station"].Value.ToString();
            station.Latitude = AprsCalc.LatLonToDouble(StatusReportDataGridView.CurrentRow.Cells["Latitude"].Value.ToString());
            station.Longitude = AprsCalc.LatLonToDouble(StatusReportDataGridView.CurrentRow.Cells["Longitude"].Value.ToString());
            StationList.GetStationList().AddStation(station);
            StationList.GetStationList().WriteXml(configure[0].DataDir + @"\stations.xml");
        }

        private void mapToolStripButton_Click(object sender, EventArgs e)
        {
            LoadMap();
        }
        #endregion
        #region " Additional methods and handlers "
        private void LoadMap()
        {
            if (map != null)
            {
                if (!map.IsDisposed)
                {
                    map.Focus();
                    return;
                }
            }
            map = new MapForm();
            map.Show();
            UpdateMapCSReports();
            UpdateStationsHeard();
        }

        private void UpdateMapCSReports()
        {
            if (map != null)
            {
                if (!map.IsDisposed)
                {
                    map.ClearCSReports();

                    var results = from res in csReports
                                  orderby res.TimeHeard descending
                                  select res;

                    var reportList = results.GroupBy(e => e.Station + e.ReportID).Select(g => g.First());

                    foreach (CSReportMessage csReport in reportList)
                    {
                        if (csReport.Latitude.Length > 0 & csReport.Longitude.Length > 0)
                        {
                            map.MapCSReport(AprsCalc.LatLonToDouble(csReport.Latitude), AprsCalc.LatLonToDouble(csReport.Longitude), csReport);
                        }
                    }
                }
            }
        }

        private void UpdateStationsHeard()
        {
            if (map != null)
            {
                if (!map.IsDisposed)
                {
                    map.ClearAprsStations();
                    foreach (AprsPacketData packet in packets)
                    {
                        if (packet.CallFrom.Length > 0)
                        {
                            map.MapStation(packet.GetAprsData(), packet.CallFrom, aprsImageList.Images[packet.SymbolIndex]);
                        }
                    }
                }
            }
            var stations =
                from station in packets
                where station.Time >= DateTime.Now.AddMinutes(-60)
                orderby station.Time descending
                select new { Time = station.Time.ToShortTimeString(), Symbol = station.SymbolCode, Index = station.SymbolIndex, station.CallFrom, station.CallTo, station.Altitude, station.AntennaGain, HAAT = station.HeightAboveAvgTerrain.ToString().Substring(0, Math.Min(station.HeightAboveAvgTerrain.ToString().Length, 5)), station.TxPower, RadioRange = station.RadioRange.ToString().Substring(0, Math.Min(station.RadioRange.ToString().Length, 6)), station.Course, station.Speed, Latitude = station.Latitude.ToString().Substring(0, Math.Min(station.Latitude.ToString().Length, 10)), Longitude = station.Longitude.ToString().Substring(0, Math.Min(station.Longitude.ToString().Length, 10)), station.Comment };

            StationsHeardDataGridView.DataSource = stations.ToList();
        }

        private void LoadMessages()
        {
            // Load data into the MessageList object.
            messages.ReadXml(configure[0].DataDir + @"\messages.xml");
        }

        private void SaveMessages()
        {
            // Write the MessageList object to an XML format file.
            messages.WriteXml(configure[0].DataDir + @"\messages.xml");
        }

        private void LoadCSReports()
        {
            // Instantiate a new CSReportList object using the messages MessageList object.
            csReports = new CSReportList(messages, (int)maxAgeNumericUpDown.Value * 60);
            // Clear the existing report information.
            detailRichTextBox.Clear();

            var results = from res in csReports
                          orderby res.TimeHeard
                          select res;

            var reportList = results.GroupBy(e => e.Station + e.ReportID).Select(g => g.Last());

            // Loop through the csReports items List.
            foreach (CSReportMessage csReportMsg in reportList)
            {
                // Add an entry in the report window for each report.
                AddLogLine(csReportMsg);
            }
            // Load the StatusReportDataGridView.
            LoadStatusReportDataGridView();

            UpdateMapCSReports();

            if (configure[0].SaveToKml)
                PublishKMLFileToolStripMenuItem_Click(this, null);
        }

        private void LoadMessageDataGridView(int folder)
        {
            selectedMessageFolder = folder;
            msgTreeView.SelectedNode = msgTreeView.Nodes[folder];

            var results =
                from res in messages
                where res.Folder == folder
                orderby res.TimeHeard descending
                select new {res.FromCall, res.ToCall, res.Message, res.TimeHeard, res.AprsMsgNumber, res.Acknowledged, res.DigiPath };

            MessageDataGridView.DataSource = results.ToList();
            MessageDataGridView.Columns["FromCall"].MinimumWidth = 65;
            MessageDataGridView.Columns["FromCall"].HeaderText = "From";
            MessageDataGridView.Columns["ToCall"].MinimumWidth = 65;
            MessageDataGridView.Columns["ToCall"].HeaderText = "To";
            MessageDataGridView.Columns["Message"].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            MessageDataGridView.Columns["Message"].HeaderText = "Message";
            MessageDataGridView.Columns["TimeHeard"].MinimumWidth = 100;
            MessageDataGridView.Columns["TimeHeard"].HeaderText = "Time Heard";
            MessageDataGridView.Columns["AprsMsgNumber"].MinimumWidth = 70;
            MessageDataGridView.Columns["AprsMsgNumber"].HeaderText = "Msg #";
            MessageDataGridView.Columns["Acknowledged"].MinimumWidth = 30;
            MessageDataGridView.Columns["Acknowledged"].HeaderText = "Ack";
            MessageDataGridView.Columns["DigiPath"].MinimumWidth = 35;
            MessageDataGridView.Columns["DigiPath"].HeaderText = "Digi Path";
        }

        private void LoadStatusReportDataGridView()
        {
            var results =
                from res in csReports
                orderby res.TimeHeard descending
                select new { res.FromCall, ReportID = res.ReportID.ToString().PadLeft(4, '0'), res.Station, IncidentTime = AprsCalc.TimeStampToDateTime(res.IncidentTime), res.StatusCode, res.TimeHeard, res.DetailText, res.StreetNumber, res.StreetDirection, res.StreetName, res.CityCode, res.StateCode, res.CrossRoad1, res.CrossRoad2, res.Latitude, res.Longitude };

            var reportList = results.GroupBy(e => e.Station + e.ReportID).Select(g => g.First());

            StatusReportDataGridView.DataSource = reportList.ToList();
            StatusReportDataGridView.Columns["ReportID"].HeaderText = "ID";
            StatusReportDataGridView.Columns["FromCall"].HeaderText = "Home Unit";
            StatusReportDataGridView.Columns["Station"].HeaderText = "Station";
            StatusReportDataGridView.Columns["StatusCode"].HeaderText = "Status Code";
            StatusReportDataGridView.Columns["TimeHeard"].HeaderText = "Time Heard";
            StatusReportDataGridView.Columns["DetailText"].HeaderText = "Details";
            StatusReportDataGridView.Columns["IncidentTime"].HeaderText = "Incident Time";
            StatusReportDataGridView.Columns["StreetNumber"].HeaderText = "Street Number";
            StatusReportDataGridView.Columns["StreetDirection"].HeaderText = "Dir";
            StatusReportDataGridView.Columns["StreetName"].HeaderText = "Street Name";
            StatusReportDataGridView.Columns["CityCode"].HeaderText = "City";
            StatusReportDataGridView.Columns["StateCode"].HeaderText = "State";
            StatusReportDataGridView.Columns["CrossRoad1"].HeaderText = "Crossroad1";
            StatusReportDataGridView.Columns["CrossRoad2"].HeaderText = "Crossroad2";
            StatusReportDataGridView.Columns["Latitude"].HeaderText = "Latitude";
            StatusReportDataGridView.Columns["Longitude"].HeaderText = "Longitude";
        }

        private void PlaySync(string fileName)
        {
            if (!configure[0].NoSound & File.Exists(fileName))
            {
                try
                {
                    SoundPlayer sound = new SoundPlayer(fileName);
                    sound.PlaySync();
                }
                catch{}
            }
        }

        private void DebugDump(string dumpString)
        {
            StreamWriter fileStream = new StreamWriter(@"c:\users\bill\desktop\dump.txt", true);
            fileStream.WriteLine(dumpString + "\r\n");
            fileStream.Close();
        }

        public void Record(AgwpeMoniUnproto data)
        {
            // Invoke a delegate to handle recording APRS data.
            Invoke(new EventHandler(delegate
            {
                //DebugDump(data.AX25Header + "\r\n" + ASCIIEncoding.UTF8.GetString(data.AX25Data));
                int folder = -1;
                AprsData pkt = new AprsData(data.AX25Data);
                DateTime timeHeard = DateTime.Now;
                string source;
                string defaultDigiPath = configure[0].DigiPath;
                
                if (pkt.ThirdPartySource.Length > 0)
                    source = pkt.ThirdPartySource.ToUpper().Trim();
                else
                    source = data.AX25CallFrom.ToUpper().Trim();

                switch (pkt.DataType)
                {
                    case AprsDataType.PositionNoTimeNoMessage:
                    case AprsDataType.PositionNoTimeWithMessage:
                    case AprsDataType.PositionWithTimeNoMessage:
                    case AprsDataType.PositionWithTimeWithMessage:
                        packets.AddPositionPacket(data);
                        UpdateStationsHeard();
                        break;
                    case AprsDataType.Message:
                        // Start out by assigning this message to the Heard Messages folder.
                        folder = 3;
                        // This variable will be used later to tell the new message object 
                        // whether or not this message is a community status report.
                        bool isCsr = false;

                        // If the message starts with the character '^' then it may contain a community status report...
                        if (pkt.MsgText.StartsWith("^"))
                        {
                            // Create a new CSReport object and parse the message.
                            CSReportMessage csrMessage = new CSReportMessage(source, pkt, timeHeard);

                            if (csrMessage.IsValidReport)
                            {
                                // This is a valid community status report.
                                isCsr = true;
                                //// Add the new report to the csReports List.
                                //csReports.AddCSReport(csrMessage);

                                //// Reload the report display.
                                //ReloadReportDataToolStripMenuItem_Click(this, null);

                                // If auto update is turned on, save the KML file.
                                if (configure[0].SaveToKml)
                                {
                                    WriteKml(Path.GetFullPath(configure[0].KmlFileLocation).TrimEnd('\\') + "\\" + configure[0].EventName.Trim() + "_features.kml");
                                    // Start a background process and, if an FTP server is configured, publish the file. 
                                    //BackgroundWorker1.RunWorkerAsync();
                                }
                            }
                        }

                        // If the source address is this stations call sign, in other words
                        // the message was sent from this station...
                        if (source == configure[0].CallSign)
                        {
                            // If the message is a community status report...
                            if (isCsr)
                                // Assign this message to the Community Status folder.
                                folder = 6;
                            else
                                // Assign this message to the Sent Messages folder.
                                folder = 2;
                        }

                        // If the message was sent to this station...
                        if (pkt.MsgAddressee.ToUpper().Trim() == configure[0].CallSign.Trim())
                        {
                            // If the message is an acknowledgement reply.
                            if (pkt.MsgText.Trim().ToUpper().StartsWith("ACK"))
                                // Don't indicate any modified folders.
                                folder = -1;
                            else
                            {
                                // If the message is a community status report...
                                if (isCsr)
                                {
                                    // Assign this message to the Community Status folder.
                                    folder = 6;
                                    PlaySync(@"sounds\Connect.wav");
                                }
                                else
                                {
                                    // Assign this message to the Inbox folder.
                                    folder = 0;
                                    PlaySync(@"sounds\Connect.wav");
                                }
                                // Then process message rules in a new thread.
                                BeginInvoke((MethodInvoker)delegate()
                                {
                                    //MessageRules.Process(radio, configDataSet.MsgRules, data, timeHeard);
                                });
                            }
                        }

                        // Regardless of what station this message is to or from, 
                        // if the message is an acknowledgement reply to a specific message number...
                        if (pkt.MsgText.Trim().ToUpper().StartsWith("ACK") & pkt.MsgText.Trim().Length > 3)
                        {
                            // Update the message and set the acknowledged flag.
                            messages.Acknowledge(source.Trim(), pkt.MsgAddressee.ToUpper().Trim(), pkt.MsgText.Trim().Substring(3));
                            SaveMessages();
                            // If the ACK is from this stations call sign...
                            if (source == configure[0].CallSign)
                                // Don't indicate any modified folders.
                                folder = -1;
                        }
                        else
                        {
                            // Record the new message.
                            messages.AddMessage(new AprsMessage(timeHeard, source, pkt.MsgAddressee, pkt.MsgText, folder, pkt.MsgNumber, data.AX25Via, isCsr));
                            SaveMessages();
                            // If the message is a community status report...
                            if (isCsr)
                                // Reload the report display.
                                ReloadReportDataToolStripMenuItem_Click(this, null);
                        }
                        if (folder == selectedMessageFolder | folder == -1)
                            LoadMessageDataGridView(selectedMessageFolder);
                        break;
                    case AprsDataType.Bulletin:
                        folder = 4;
                        messages.AddMessage(new AprsMessage(timeHeard, source, pkt.MsgAddressee, pkt.MsgText, folder, pkt.MsgNumber, data.AX25Via, false));
                        SaveMessages();
                        break;
                    case AprsDataType.Announcement:
                        folder = 5;
                        messages.AddMessage(new AprsMessage(timeHeard, source, pkt.MsgAddressee, pkt.MsgText, folder, pkt.MsgNumber, data.AX25Via, false));
                        SaveMessages();
                        break;
                    case AprsDataType.Object:
                    case AprsDataType.Item:
                        break;
                }
                if (folder >= 0)
                {
                    // If the message is stored in a folder that is not currently selected...
                    if (folder != selectedMessageFolder)
                        // Set the folder node fontstyle to bold;
                        msgTreeView.Nodes[folder].NodeFont = new Font(msgTreeView.Nodes[folder].NodeFont.FontFamily, msgTreeView.Nodes[folder].NodeFont.SizeInPoints, FontStyle.Bold);
                    // If this was an inbound message with a message number...
                    if (pkt.MsgAddressee.ToUpper().Trim() == configure[0].CallSign.Trim() & pkt.MsgNumber.Trim().Length > 0 & source != configure[0].CallSign)
                    {
                        // Acknowledge receipt of the message.
                        if (radio.SendAck(source, defaultDigiPath.ToUpper(), pkt.MsgNumber.Trim()))
                        {
                            // Record the acknowledgement.
                            messages.Acknowledge(source, defaultDigiPath.ToUpper(), pkt.MsgNumber.Trim());
                            // Reload messages in the Inbox folder.
                            if (selectedMessageFolder == 0)
                                LoadMessageDataGridView(0);
                        }
                    }
                }
            }));
        }

        public void UpdateConnectButton()
        {
            // Invoke a delegate to modify interface components.
            Invoke(new EventHandler(delegate
            {
                agwConnectToolStripButton.Image = buttonImageList.Images["connect.png"];
                agwConnectToolStripButton.ToolTipText = "Disconnect from AGWPE";
                statusBarLabel.Text = "Connected to AGWPE";
                beaconToolStripButton.Enabled = true;
                beaconToolStripButton.ToolTipText = "Enable Beacon Transmissions";
                newMessageToolStripButton.Enabled = true;
                replyToolStripButton.Enabled = true;
                forwardToolStripButton.Enabled = true;
                PlaySync(@"sounds\Activate.wav");
            }));
        }

        private void PrintDocument_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
        {
            printMore = 0;
        }

        private void PrintDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
        {
            // Print the content of RichTextBox and record the last character printed.
            printMore = detailRichTextBox.Print(printMore, detailRichTextBox.TextLength, e);

            // Check for more pages.
            if (printMore < detailRichTextBox.Text.Length)
                e.HasMorePages = true;
            else
                e.HasMorePages = false;
        }

        private string GetStatusString(string statusCode)
        {
            string statusString = "";

            if (statusCode.Trim().Length == 16)
            {
                int index = 0;
                byte[] statusBytes = new byte[15];

                statusBytes = ASCIIEncoding.UTF8.GetBytes(statusCode);
                while (index < 16)
                {
                    // 87 Is the ASCII value for the letter W which is the default value for each status code.
                    if (statusBytes[index] != 87)
                        // The position in the array correlates to the category number.
                        statusString = statusString + " " + (index + 1).ToString() + ((char)statusBytes[index]).ToString();
                    index += 1;
                }
            }
            return statusString;
        }

        private string GetStatusDetailString(string statusCode)
        {
            string statusString = "";

            if (statusCode.Trim().Length == 16)
            {
                int index = 0;
                byte[] statusBytes = new byte[15];

                statusBytes = ASCIIEncoding.UTF8.GetBytes(statusCode);
                while (index < 16)
                {
                    // 87 Is the ASCII value for the letter W which is the default value for each status code.
                    if (statusBytes[index] != 87)
                    {
                        statusString = statusString + (index + 1).ToString() + ((char)statusBytes[index]).ToString() + " - ";
                        switch (index)
                        {
                            case 0:
                                statusString = statusString + statusText[0].Status1;
                                break;
                            case 1:
                                statusString = statusString + statusText[0].Status2;
                                break;
                            case 2:
                                statusString = statusString + statusText[0].Status3;
                                break;
                            case 3:
                                statusString = statusString + statusText[0].Status4;
                                break;
                            case 4:
                                statusString = statusString + statusText[0].Status5;
                                break;
                            case 5:
                                statusString = statusString + statusText[0].Status6;
                                break;
                            case 6:
                                statusString = statusString + statusText[0].Status7;
                                break;
                            case 7:
                                statusString = statusString + statusText[0].Status8;
                                break;
                            case 8:
                                statusString = statusString + statusText[0].Status9;
                                break;
                            case 9:
                                statusString = statusString + statusText[0].Status10;
                                break;
                            case 10:
                                statusString = statusString + statusText[0].Status11;
                                break;
                            case 11:
                                statusString = statusString + statusText[0].Status12;
                                break;
                            case 12:
                                statusString = statusString + statusText[0].Status13;
                                break;
                            case 13:
                                statusString = statusString + statusText[0].Status14;
                                break;
                            case 14:
                                statusString = statusString + statusText[0].Status15;
                                break;
                            case 15:
                                statusString = statusString + statusText[0].Status16;
                                break;
                        }
                        statusString = statusString + "<br/>";
                    }
                    index += 1;
                }
            }
            return statusString;
        }

        private string GetLocationString(CSReportMessage csReportMessage)
        {
            string location = "";
            Coordinate lat = new Coordinate(AprsCalc.LatLonToDouble(csReportMessage.Latitude));
            Coordinate lon = new Coordinate(AprsCalc.LatLonToDouble(csReportMessage.Longitude));

            if (csReportMessage.StreetNumber.Length > 0)
                location += csReportMessage.StreetNumber + " ";

            if (csReportMessage.StreetDirection.Length > 0)
                location += csReportMessage.StreetDirection + " ";

            if (csReportMessage.StreetName.Length > 0)
                location += csReportMessage.StreetName + " ";

            if (csReportMessage.CityCode.Length > 0)
                location += csReportMessage.CityCode + " ";

            if (csReportMessage.CrossRoad1.Length > 0 & csReportMessage.CrossRoad2.Length > 0)
                location += csReportMessage.CrossRoad1 + " and " + csReportMessage.CrossRoad2 + " ";

            if (csReportMessage.Latitude.Length > 0 & csReportMessage.Longitude.Length > 0)
                location += "(" + lat.ToDecMinuteString() + " " + lon.ToDecMinuteString() + ")";

            return location.TrimEnd(' ');
        }

        private string GetIconColor(CSReportMessage csReportMessage)
        {
            if(csReportMessage.StatusCode.Contains("R"))
                return "esp_red";

            if (csReportMessage.StatusCode.Contains("Y"))
                return "esp_yellow";

            if (csReportMessage.StatusCode.Contains("G"))
                return "esp_green";

            return "esp_white";
        }

        private void AddLogLine(CSReportMessage csReportMsg)
        {
            int index = 0;
            byte[] statusBytes = new byte[15];

            // Unselect any selected text
            //detailRichTextBox.SelectedText = " ";
            // Set the font size and style
            detailRichTextBox.SelectionFont = new Font(detailRichTextBox.Font.FontFamily, 10, FontStyle.Bold);
            // Set the font color
            detailRichTextBox.SelectionColor = Color.Blue;
            // Write the string to the RichTextBox
            detailRichTextBox.AppendText(csReportMsg.TimeHeard.ToString() + " ----------------------------------------\n");
            // Set the font size and style
            detailRichTextBox.SelectionFont = new Font(detailRichTextBox.Font.FontFamily, 8, FontStyle.Bold);
            // Set the font color
            detailRichTextBox.SelectionColor = Color.Black;
            // Write the status string to the RichTextBox
            detailRichTextBox.AppendText(csReportMsg.Station + " (" + csReportMsg.ReportID.ToString().PadLeft(4, '0') + "), " + GetLocationString(csReportMsg) + ", " + GetStatusString(csReportMsg.StatusCode) + ", " + AprsCalc.TimeStampToDateTime(csReportMsg.IncidentTime).ToShortTimeString() + "\n");
            // Write the status code details to the RichTextBox
            statusBytes = ASCIIEncoding.UTF8.GetBytes(csReportMsg.StatusCode);
            if (csReportMsg.StatusCode.Trim().Length >= 16 & csReportMsg.StatusCode.Trim().Length <= 22)
            {
                while (index < 16)
                {
                    //87 Is the ASCII value for the letter W which is the default value for each status code.
                    if (statusBytes[index] != 87)
                    {
                        // The position in the array correlates to the category number.
                        switch (statusBytes[index])
                        {
                            case 82: // Red
                                detailRichTextBox.SelectionColor = Color.Red;
                                break;
                            case 89: // Yellow
                                detailRichTextBox.SelectionColor = Color.Orange;
                                break;
                            case 71: // Green
                                detailRichTextBox.SelectionColor = Color.Green;
                                break;
                        }
                        detailRichTextBox.AppendText((index + 1).ToString() + ((char)statusBytes[index]).ToString() + " - ");
                        switch (index)
                        {
                            case 0:
                                detailRichTextBox.AppendText(statusText[0].Status1 + "\n");
                                break;
                            case 1:
                                detailRichTextBox.AppendText(statusText[0].Status2 + "\n");
                                break;
                            case 2:
                                detailRichTextBox.AppendText(statusText[0].Status3 + "\n");
                                break;
                            case 3:
                                detailRichTextBox.AppendText(statusText[0].Status4 + "\n");
                                break;
                            case 4:
                                detailRichTextBox.AppendText(statusText[0].Status5 + "\n");
                                break;
                            case 5:
                                detailRichTextBox.AppendText(statusText[0].Status6 + "\n");
                                break;
                            case 6:
                                detailRichTextBox.AppendText(statusText[0].Status7 + "\n");
                                break;
                            case 7:
                                detailRichTextBox.AppendText(statusText[0].Status8 + "\n");
                                break;
                            case 8:
                                detailRichTextBox.AppendText(statusText[0].Status9 + "\n");
                                break;
                            case 9:
                                detailRichTextBox.AppendText(statusText[0].Status10 + "\n");
                                break;
                            case 10:
                                detailRichTextBox.AppendText(statusText[0].Status11 + "\n");
                                break;
                            case 11:
                                detailRichTextBox.AppendText(statusText[0].Status12 + "\n");
                                break;
                            case 12:
                                detailRichTextBox.AppendText(statusText[0].Status13 + "\n");
                                break;
                            case 13:
                                detailRichTextBox.AppendText(statusText[0].Status14 + "\n");
                                break;
                            case 14:
                                detailRichTextBox.AppendText(statusText[0].Status15 + "\n");
                                break;
                            case 15:
                                detailRichTextBox.AppendText(statusText[0].Status16 + "\n");
                                break;
                        }
                    }
                    index += 1;
                }
            }
            // If there are report details, print them next.
            if(csReportMsg.DetailText.Length > 0)
            {
                // Set the font size and style.
                detailRichTextBox.SelectionFont = new Font(detailRichTextBox.Font.FontFamily, 8, FontStyle.Regular);
                // Set the font color.
                detailRichTextBox.SelectionColor = Color.DarkBlue;
                // Write the string to the RichTextBox.
                detailRichTextBox.AppendText(csReportMsg.DetailText + "\n");
            }

            // Write a carriage return to the RichTextBox.
            detailRichTextBox.AppendText("\n");
            // Scroll down, if needed, so we can see the new text.
            detailRichTextBox.ScrollToCaret();
        }

        private void WriteKml(string fileName)
        {
            if (Directory.Exists(Path.GetDirectoryName(fileName)))
            {
                StreamWriter fileStream;
                int rowNum = 0;
                double midLatitude = 0;
                double midLongitude = 0;

                midLatitude = csReports.GetMidLat();
                midLongitude = csReports.GetMidLon();

                try
                { 
                    // Create a new StreamWriter object to write to the file with.
                    fileStream = new StreamWriter(fileName);
                    // Setup the document and icon styles.
                    fileStream.Write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\r\n" +
                                     "<kml xmlns=\"http://earth.google.com/kml/2.1\">" + "\r\n" +
                                     "<Document>" + "\r\n" +
                                     "    <name>" + fileName + "</name>" + "\r\n" +
                                     "    <LookAt>" + "\r\n" +
                                     "        <longitude>" + midLongitude.ToString() + "</longitude>" + "\r\n" +
                                     "        <latitude>" + midLatitude.ToString() + "</latitude>" + "\r\n" +
                                     "        <altitude>0</altitude>" + "\r\n" +
                                     "        <range>" + Math.Ceiling(configure[0].KmlRange / 3.28084).ToString() + "</range>" + "\r\n" +
                                     "        <tilt>0</tilt>" + "\r\n" +
                                     "        <heading>0</heading>" + "\r\n" +
                                     "    </LookAt>" + "\r\n" +
                                     "    <Style id=\"esp_red\">" + "\r\n" +
                                     "	      <IconStyle>" + "\r\n" +
                                     "            <scale>1.1</scale>" + "\r\n" +
                                     "            <Icon>" + "\r\n" +
                                     "				<href>" + configure[0].KmlRedIcon + "</href>" + "\r\n" +
                                     "			</Icon>" + "\r\n" +
                                     "			<hotSpot x=\"16\" y=\"16\" xunits=\"pixels\" yunits=\"pixels\"/>" + "\r\n" +
                                     "		</IconStyle>" + "\r\n" +
                                     "	</Style>" + "\r\n" +
                                     "   <Style id=\"esp_yellow\">" + "\r\n" +
                                     "        <IconStyle>" + "\r\n" +
                                     "            <scale>1.1</scale>" + "\r\n" +
                                     "            <Icon>" + "\r\n" +
                                     "                <href>" + configure[0].KmlYellowIcon + "</href>" + "\r\n" +
                                     "            </Icon>" + "\r\n" +
                                     "            <hotSpot x=\"16\" y=\"16\" xunits=\"pixels\" yunits=\"pixels\"/>" + "\r\n" +
                                     "        </IconStyle>" + "\r\n" +
                                     "    </Style>" + "\r\n" +
                                     "    <Style id=\"esp_green\">" + "\r\n" +
                                     "        <IconStyle>" + "\r\n" +
                                     "            <scale>1.1</scale>" + "\r\n" +
                                     "            <Icon>" + "\r\n" +
                                     "                <href>" + configure[0].KmlGreenIcon + "</href>" + "\r\n" +
                                     "            </Icon>" + "\r\n" +
                                     "            <hotSpot x=\"16\" y=\"16\" xunits=\"pixels\" yunits=\"pixels\"/>" + "\r\n" +
                                     "        </IconStyle>" + "\r\n" +
                                     "    </Style>" + "\r\n" +
                                     "    <Style id=\"esp_white\">" + "\r\n" +
                                     "        <IconStyle>" + "\r\n" +
                                     "            <scale>1.1</scale>" + "\r\n" +
                                     "            <Icon>" + "\r\n" +
                                     "                <href>" + configure[0].KmlWhiteIcon + "</href>" + "\r\n" +
                                     "            </Icon>" + "\r\n" +
                                     "            <hotSpot x=\"16\" y=\"16\" xunits=\"pixels\" yunits=\"pixels\"/>" + "\r\n" +
                                     "        </IconStyle>" + "\r\n" +
                                     "    </Style>" + "\r\n");

                    // Loop through all report records and create points for status reports with geo data.
                    var reports = 
                        from report in csReports
                        where report.Latitude.Trim().Length == 8 & report.Longitude.Trim().Length == 9
                        select report;

                    foreach(CSReportMessage report in reports)
                    {
                        // If the report contains latitude and longitude data, create a point for it.
                        if (AprsCalc.LatLonToDouble(report.Longitude) != 0 & AprsCalc.LatLonToDouble(report.Latitude) != 0)
                        {
                            fileStream.Write("    <Placemark id=\"" +
                                         report.ReportID.ToString() + "\">" + "\r\n" +
                                         "        <name>#" +
                                         report.ReportID.ToString() + " - " +
                                         report.TimeHeard.ToString() + "</name>" + "\r\n" +
                                         "        <description><![CDATA[" +
                                         GetLocationString(report) + "<br/>" +
                                         report.DetailText + "<br/>" +
                                         GetStatusDetailString(report.StatusCode.Trim()) + "]]></description>" + "\r\n" +
                                         "        <styleUrl>#" + GetIconColor(report) + "</styleUrl>" + "\r\n" +
                                         "        <Point>" + "\r\n" +
                                         "            <coordinates>" +
                                         AprsCalc.LatLonToDouble(report.Longitude).ToString() + "," +
                                         AprsCalc.LatLonToDouble(report.Latitude).ToString() + ",0</coordinates>" + "\r\n" +
                                         "        </Point>" + "\r\n" +
                                         "    </Placemark>" + "\r\n");
                        }
                        rowNum += 1;
                    }
                    // End the document.
                    fileStream.Write("</Document>" + "\r\n" +
                                     "</kml>");
                    fileStream.Close();
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message);
                }
            }
        }

        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        private void BackgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            if(configure[0].FtpKmlPublish & configure[0].FtpKmlHost.Trim().Length > 0 & configure[0].FtpKmlPath.Trim().Length > 0 & configure[0].FtpKmlLogin.Trim().Length > 0 & configure[0].FtpKmlPassword.Trim().Length > 0)
            {
                //FTP ftpKML;

                //ftpKML = new FTP(configure[0].FtpKmlHost, configure[0].FtpKmlPath, configure[0].FtpKmlUser, configure[0].FtpKmlPassword, configure[0].FtpKmlPort);
                //BackgroundWorker1.ReportProgress(1);
                //// Attempt to log into the FTP Server.
                //if(ftpKML.Login())
                //{
                //    BackgroundWorker1.ReportProgress(2);
                //    // Set the FTP mode to binary
                //    ftpKML.SetBinaryMode(true);
                //    BackgroundWorker1.ReportProgress(3);
                //    // Upload the feature file.
                //    ftpKML.UploadFile(Path.GetFullPath(configure[0].KmlFileLocation).TrimEnd('\\') + "\\" + configure[0].EventName.Trim() + "_features.kml");
                //    BackgroundWorker1.ReportProgress(4);
                //    ftpKML.CloseConnection();
                //    BackgroundWorker1.ReportProgress(5);
                //}
            }
        }

        private void BackgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
        }

        private void BackgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
        {
            switch (e.ProgressPercentage)
            {
                case 1:
                    statusBarLabel.Text = "Connecting to FTP server.";
                    break;
                case 2:
                    statusBarLabel.Text = "Setting mode to binary.";
                    break;
                case 3:
                    statusBarLabel.Text = "Uploading file.";
                    break;
                case 4:
                    statusBarLabel.Text = "Closing connection.";
                    break;
                case 5:
                    statusBarLabel.Text = "File transfer complete.";
                    break;
                default:
                    statusBarLabel.Text = "ERROR uploading file.";
                    break;
            }
        }

        private void StatusReportDataGridView_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            DateTime incidentTime;
            ReportForm rf = new ReportForm();

            if (DateTime.TryParse(((DataGridViewRow)StatusReportDataGridView.CurrentRow).Cells["IncidentTime"].Value.ToString(), out incidentTime))
            {
                if(incidentTime < DateTime.Now.AddMinutes(-1440))
                {
                    if (MessageBox.Show("The selected incident is more than 24 hours old.\n\nWould you like to create a new status report with current status information.", "Unable to Edit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    {
                        rf.EditReportGridData((DataGridViewRow)StatusReportDataGridView.CurrentRow, true);
                        rf.Show();
                    }
                    return;
                }
            }
            rf.EditReportGridData((DataGridViewRow)StatusReportDataGridView.CurrentRow, false);
            rf.Show();
        }

        private void msgTreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            // Set the folder node fontstyle to regular;
            msgTreeView.Nodes[selectedMessageFolder].NodeFont = new Font(msgTreeView.Nodes[selectedMessageFolder].NodeFont.FontFamily, msgTreeView.Nodes[selectedMessageFolder].NodeFont.Size, FontStyle.Regular);
            // This line of code loads data into the MessageList object.
            LoadMessageDataGridView(e.Node.Index);
        }

        private void MessageDataGridView_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                for (int index = 0; index < MessageDataGridView.SelectedRows.Count; index++)
                {
                    messages.Delete(MessageDataGridView.SelectedRows[index].Cells[0].Value.ToString(),
                        MessageDataGridView.SelectedRows[index].Cells[4].Value.ToString());
                }
                SaveMessages();
                LoadMessageDataGridView(selectedMessageFolder);
                if(selectedMessageFolder == 6)
                    LoadCSReports();
            }
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (MessageBox.Show("You are about to exit CSReporter.\n\nClick OK to continue.", "Confirm", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
                radio.Close();
            else
                e.Cancel = true;
        }
        #endregion
    }
}
